//
// Unihan Input Method
//
// Copyright © 2002-2007 Sun Wah Linux Ltd.
//
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
// USA
//
// $Id: im_methods.c 817 2007-08-24 16:31:08Z roger $
//

#include <config.h>
#include "im_methods.h"
#include "core.h"
#include "unicode.h"
#include "unihan_xaux.h"
#include "tc_sc.h"
#include "ui.h"
#include "phrase.h"
#include "log.h"
#include <string.h>
#include <stdio.h>
#include <stdarg.h>

#if NEW_PROPERTY
static int properties_register (UnihanIM *uim, const UnihanPropertyList *property_list, char *imid);
static int properties_update (UnihanIM *uim, const UnihanPropertyList *property_list);
#endif

static int send_buffer(UnihanIM *uim, void *, size_t);
static int config_get(UnihanIM *uim, char *key, char **value, long *len);
static int config_set(UnihanIM *uim, char *key, char *value, long len);
static int preedit_draw(UnihanIM *uim, char *string, UnihanStyleList *styles);
static int preedit_caret_pos(UnihanIM *uim, int caretPos);
static int preedit_change(UnihanIM *uim, char *string, UnihanStyleList *styles, int pos, int len, int caretPos);
static int preedit_erase(UnihanIM *uim);
static int status_draw(UnihanIM *uim, char *string, UnihanStyleList *styles);
static int locale_get(UnihanIM *uim, char **locale);
static int tcsc_mode_get(UnihanIM *uim, UnihanTCSCMode *tcscMode);
static int tcsc_do_conv(UnihanIM *uim, UnihanTCSCMode tcscMode, char *orig, char **converted);
static int mem_alloc(UnihanIM *uim, size_t len, void **mem);
static int client_info_get(UnihanIM *uim, UnihanClientInfoType type, char **info);

#if NEW_PROPERTY
#else
static int is_fullwidth(UnihanIM *uim);
static int is_chinesepunc(UnihanIM *uim);
static int is_shuangpin(UnihanIM *uim);
#endif

static int beep(UnihanIM *uim);
static void im_log(UnihanIM *uim, UnihanLogLevelFlags log_level, const char *format, ...);

UnihanMethods unihan_methods_table = {
#if NEW_PROPERTY
	properties_register,
	properties_update,
#endif
	send_buffer,
	config_get,
	config_set,
	preedit_draw,
	preedit_caret_pos,
	preedit_change,
	preedit_erase,
	lookup_start,
	lookup_draw,
	lookup_done,
	status_draw,
	commit_string,
	locale_get,
	tcsc_mode_get,
	tcsc_do_conv,
	mem_alloc,
	client_info_get,
#if NEW_PROPERTY
#else
	is_fullwidth,
	is_chinesepunc,
	is_shuangpin,
#endif
	beep,
	match_phrase,
    im_log
};

#if NEW_PROPERTY
/***************************************************************************/
static int
properties_register (UnihanIM *uim, const UnihanPropertyList *property_list, char *imid)
{
	iml_session_t *s;
	UnihanPropertyList *currIMpropList;
	int int_values[21];
    UTFCHAR *string_values[21];
    int num_string = 1;
	
	if (!uim) return 0;

	s = (iml_session_t *)uim->privateData;
	if (!s) return 0;

	int_values[0] = UNIHAN_AUX_PROPERTY_REGISTER;
	string_values[0] = imid;
	
	num_string = convert_props_to_string(s,
					property_list,
					int_values,
					string_values);
        
	aux_draw(s, AUX_STATUS_CLASS_NAME, num_string, int_values, num_string, string_values);
	
	return 1;
}

static int
properties_update (UnihanIM *uim, const UnihanPropertyList *property_list)
{
	iml_session_t *s;
	UnihanPropertyList *currIMpropList;
	int int_values[21];
    UTFCHAR *string_values[21];
    int num_string = 1;
	
	if (!uim) return 0;

	s = (iml_session_t *)uim->privateData;
	if (!s) return 0;

	int_values[0] = UNIHAN_AUX_PROPERTY_UPDATE;
	string_values[0] = "null";	
	
	num_string = convert_props_to_string(s,
					property_list,
					int_values,
					string_values);
        
	aux_draw(s, AUX_STATUS_CLASS_NAME, num_string, int_values, num_string, string_values);
	
	return 1;
}


#else

static int
is_fullwidth(UnihanIM *uim)
{
        iml_session_t *s;
        DataPerSession *ds;

        if (!uim) return 0;
        s = (iml_session_t *)uim->privateData;
        if (!s) return 0;
        ds = (DataPerSession *)s->specific_data;
        return ds->fullwidth;
}

static int
is_chinesepunc(UnihanIM *uim)
{
	iml_session_t *s;
	DataPerSession *ds;

	if (!uim) return 0;	// return 0 is not a good idea,
				//le don't know iml_session's wrong. better return -1
	s = (iml_session_t *)uim->privateData;
	if (!s) return 0;
	ds = (DataPerSession *)s->specific_data;
	return ds->puncce;
}

static int
is_shuangpin(UnihanIM *uim)
{
	iml_session_t *s;
	DataPerSession *ds;

	if (!uim) return 0;
	s = (iml_session_t *)uim->privateData;
	if (!s) return 0;
	ds = (DataPerSession *)s->specific_data;
	return 0;
	//return ds->shuangpin;
}
#endif

int
commit_string(UnihanIM *uim, char *string)
{
	if (!uim || !string)
        return 0;
	iml_session_t *s = (iml_session_t *)uim->privateData;
	if (!s)
        return 0;

	IMText _imt, *imt = &_imt;
	memset(imt, 0, sizeof(IMText));

    UTFCHAR *string_u16 = utf8_to_utf16be(s, (UTF8CHAR *)string, &_imt.char_length);
	imt->encoding = UTF16_CODESET;
	imt->text.utf_chars = string_u16;
    imt->feedback = feedback_new(s, imt->char_length);

	iml_inst *lp = s->If->m->iml_make_commit_inst(s, imt);
	s->If->m->iml_execute(s, &lp);

    int int_values[1] = { UNIHAN_AUX_COMMITTED_STRING };
    UTFCHAR *string_values[1] = { string_u16 };
    aux_draw(s, AUX_LOOKUP_CLASS_NAME, 1, int_values, 1, string_values);

	return imt->char_length;
}

int
match_phrase(UnihanIM *uim, char *string)
{
	if (!uim) return 0;
	if (!string) return 0;
	iml_session_t *s = (iml_session_t *)uim->privateData;
	if (!s) return 0;
	DataPerSession *ds = (DataPerSession *)s->specific_data;
    DataPerDesktop *dd = (DataPerDesktop *)s->desktop->specific_data;

	if (dd->phrase_mode != 0) {
		// lookup related phrase
		if (phrase_matcher_search(s, (UTF8CHAR *)string) > 0) {
			ds->phrase_state = PHRASE_STATE_SELECTING;
			// we only note that we need to show phrase
			// lookup here.
			// we don't actually show the lookup until
			// im_propagate_event() returns
		} else {
			ds->phrase_state = PHRASE_STATE_IDLE;
		}
	}
	return ((ds->phrase_state == PHRASE_STATE_SELECTING) ? 1 : 0);
}

static void
im_log(UnihanIM *uim, UnihanLogLevelFlags log_level, const char *format, ...)
{
    va_list args;
    va_start(args, format);
    char log_domain[256];
    snprintf(log_domain, 255, "%s/%s", uim->module_id, uim->id);
    unihan_logv(log_domain, (LogLevelFlags)log_level, format, args);
    va_end(args);
}

#if USE_DEFAULT_PREEDIT
static int
preedit_draw(UnihanIM *uim, char *string, UnihanStyleList *styles)
{
	iml_session_t *s;
	IMText *imt = NULL;
	iml_inst *lp, *rrv = NULL;
	DataPerSession *ds = NULL;
	
	if (!uim) return 1;
	if (!string) return 1;
	//FIXME: if (!styles) return 0;
	s = (iml_session_t *)uim->privateData;
	if (!s) return 1;
	ds = (DataPerSession *)s->specific_data;

    if (!is_preedit_started(ds->ui_started)) {
		lp = s->If->m->iml_make_preedit_start_inst(s);
		s->If->m->iml_link_inst_tail(&rrv, lp);
        ds->ui_started |= PREEDIT_STARTED;
    }

    imt = s->If->m->iml_new(s, sizeof(IMText));
	memset(imt, 0, sizeof(IMText));
	imt->encoding = UTF16_CODESET;
	imt->text.utf_chars = utf8_to_utf16be(s, (unsigned char *)string, &imt->char_length);

	// FIXME: should use the styles list
	imt->feedback = feedback_new(s, imt->char_length);

	lp = s->If->m->iml_make_preedit_draw_inst(s, imt);
	s->If->m->iml_link_inst_tail(&rrv, lp);
	s->If->m->iml_execute(s, &rrv);

	return 0;
}
#else
static int
preedit_draw(UnihanIM *uim, char *string, UnihanStyleList *styles)
{
	iml_session_t *s;
	DataPerSession *ds = NULL;
	int int_values[1];
        UTFCHAR **string_values = NULL;
	
	if (!uim) return 1;
	if (!string) return 1;
	//FIXME: if (!styles) return 0;
	s = (iml_session_t *)uim->privateData;
	if (!s) return 1;
	ds = (DataPerSession *)s->specific_data;

    ds->ui_started |= PREEDIT_STARTED;

	// FIXME: should use the styles list
	int_values[0] = UNIHAN_AUX_PREEDIT_DRAW;
	string_values = (UTFCHAR **)s->If->m->iml_new(s, sizeof(UTFCHAR*));
	string_values[0] = utf8_to_utf16be(s, (UTF8CHAR *)string, NULL);
	aux_draw(s, AUX_LOOKUP_CLASS_NAME, 1, int_values, 1, string_values);
	return 0;
}
#endif

#if USE_DEFAULT_PREEDIT
static int 
preedit_caret_pos(UnihanIM *uim, int caretPos)
{
	iml_session_t *s;
	iml_inst *lp, *rrv = NULL;
	DataPerSession *ds = NULL;

	if (!uim) return 1;
	s = (iml_session_t *)uim->privateData;
	if (!s) return 1;
	ds = (DataPerSession *)s->specific_data;

    if (!is_preedit_started(ds->ui_started)) {
		lp = s->If->m->iml_make_preedit_start_inst(s);
		s->If->m->iml_link_inst_tail(&rrv, lp);
        ds->ui_started |= PREEDIT_STARTED;
	}

	lp = s->If->m->iml_make_preedit_caret_inst(s, caretPos);
	s->If->m->iml_link_inst_tail(&rrv, lp);
	s->If->m->iml_execute(s, &rrv);
	
	return 0;
}
#else
static int 
preedit_caret_pos(UnihanIM *uim, int caretPos)
{
	iml_session_t *s;
	DataPerSession *ds = NULL;

	if (!uim) return 1;
	s = (iml_session_t *)uim->privateData;
	if (!s) return 1;
	ds = (DataPerSession *)s->specific_data;

    ds->ui_started |= PREEDIT_STARTED;

    int int_values[2];
    int_values[0] = UNIHAN_AUX_PREEDIT_CARET;
    int_values[1] = caretPos;
    aux_draw(s, AUX_LOOKUP_CLASS_NAME, 2, int_values, 0, NULL);
	
	return 0;
}
#endif

#if USE_DEFAULT_PREEDIT
static int 
preedit_change(UnihanIM *uim, char *string, UnihanStyleList *styles, int pos, int len, int caretPos)
{
	iml_session_t *s;
	IMText _imt, *imt = &_imt;
	iml_inst *lp, *rrv = NULL;
	DataPerSession *ds = NULL;

	if (!uim) return 1;
	if (!string) return 1;
	// FIXME: if (!styles) return 1;
	s = (iml_session_t *)uim->privateData;
	if (!s) return 1;
	ds = (DataPerSession *)s->specific_data;

    if (!is_preedit_started(ds->ui_started)) {
		lp = s->If->m->iml_make_preedit_start_inst(s);
		s->If->m->iml_link_inst_tail(&rrv, lp);
        ds->ui_started |= PREEDIT_STARTED;
	}

	memset(imt, 0, sizeof(IMText));
	imt->encoding = UTF16_CODESET;
	imt->text.utf_chars = utf8_to_utf16be(s, (unsigned char *)string, &imt->char_length);

	// FIXME: use styles
	imt->feedback = feedback_new(s, imt->char_length);

	lp = s->If->m->iml_make_preedit_draw_with_chgpos_inst(s, imt, pos, len, caretPos);
	s->If->m->iml_link_inst_tail(&rrv, lp);
	s->If->m->iml_execute(s, &rrv);
	
	return 0;
}
#else
static int 
preedit_change(UnihanIM *uim, char *string, UnihanStyleList *styles, int pos, int len, int caretPos)
{
	iml_session_t *s;
	DataPerSession *ds = NULL;

	if (!uim) return 1;
	if (!string) return 1;
	// FIXME: if (!styles) return 1;
	s = (iml_session_t *)uim->privateData;
	if (!s) return 1;
	ds = (DataPerSession *)s->specific_data;

    ds->ui_started |= PREEDIT_STARTED;
	return 0;
}
#endif

#if USE_DEFAULT_PREEDIT
static int 
preedit_erase(UnihanIM *uim)
{
	iml_session_t *s;
	iml_inst *lp, *rrv = NULL;
	DataPerSession *ds = NULL;
	
	if (!uim) return 1;
	s = (iml_session_t *)uim->privateData;
	if (!s) return 1;
	ds = (DataPerSession *)s->specific_data;

    if (!is_preedit_started(ds->ui_started))
        return 1;

	lp = s->If->m->iml_make_preedit_erase_inst(s);
	s->If->m->iml_link_inst_tail(&rrv, lp);
//        lp = s->If->m->iml_make_preedit_done_inst(s);
//        s->If->m->iml_link_inst_tail(&rrv, lp);
	s->If->m->iml_execute(s, &rrv);
    ds->ui_started &= ~PREEDIT_STARTED;

	return 0;
}
#else
static int 
preedit_erase(UnihanIM *uim)
{
	iml_session_t *s;
	DataPerSession *ds = NULL;
	int int_values[1];
	
	if (!uim) return 1;
	s = (iml_session_t *)uim->privateData;
	if (!s) return 1;
	ds = (DataPerSession *)s->specific_data;

    if (!is_preedit_started(ds->ui_started))
        return 1;

    ds->ui_started &= ~PREEDIT_STARTED;
	int_values[0] = UNIHAN_AUX_PREEDIT_HIDE;
	aux_draw(s, AUX_LOOKUP_CLASS_NAME, 1, int_values, 0, NULL);

	return 0;
}
#endif


int 
lookup_start(UnihanIM *uim, UnihanLookupStart *lustart)
{
	iml_session_t *s;
	iml_inst *lp, *rrv = NULL;
	IMLookupStartCallbackStruct *start = NULL;

	if (!uim) return 1;
	if (!lustart) return 1;
	s = (iml_session_t *)uim->privateData;
	if (!s) return 1;

	start = s->If->m->iml_new(s, sizeof(IMLookupStartCallbackStruct));
	memset(start, 0, sizeof(IMLookupStartCallbackStruct));
	start->whoIsMaster = IMIsMaster;
	start->IMPreference = (LayoutInfo *)s->If->m->iml_new(s, sizeof(LayoutInfo));
	start->IMPreference->choice_per_window = lustart->choicePerWindow;
	start->IMPreference->ncolumns = lustart->numColumns;
	start->IMPreference->nrows = lustart->numRows;
	if (lustart->direction == UNIHAN_LOOKUP_DIRECTION_RIGHT) {
		start->IMPreference->drawUpDirection = DrawUpHorizontally;
	} else {
		start->IMPreference->drawUpDirection = DrawUpVertically;
	}
	start->IMPreference->whoOwnsLabel = IMOwnsLabel;
	start->CBPreference = NULL;

	lp = s->If->m->iml_make_lookup_start_inst(s, start);
	s->If->m->iml_link_inst_tail(&rrv, lp);
	s->If->m->iml_execute(s, &rrv);

    DataPerSession *ds = (DataPerSession *)s->specific_data;
    ds->lookup_cands_per_page = lustart->choicePerWindow;

	return 0;
}


static int
lookup_draw_aux(UnihanIM *uim, UnihanLookupDraw *lud)
{
    iml_session_t *s = (iml_session_t *)uim->privateData;
    int int_values[8];
    UTFCHAR **string_values = NULL;
    int i;
    DataPerSession *ds = (DataPerSession *)s->specific_data;

    if (!ds->lookup_cands_per_page) {
        ds->lookup_cands_per_page = 9;
    }
    int page_size = ds->lookup_cands_per_page;

    int_values[0] = UNIHAN_AUX_LOOKUP_DRAW;
    int_values[1] = lud->numChoices;
    int_values[2] = lud->mode; // 0 = normal; 1 = phrase FIXME
    int_values[3] = 0; // FIXME: bitfield, up arrow/down arrow
    int_values[4] = lud->totalChoices;
    int_values[5] = lud->currentChoice;
    int_values[6] = lud->selected;
    int_values[7] = page_size;

    // can we go back?
    if (lud->currentChoice >= page_size)
        int_values[3] |= 0x02;
    // can we go forward?
    if ((lud->currentChoice/page_size)*page_size + page_size < lud->totalChoices)
        int_values[3] |= 0x01;

    string_values = (UTFCHAR **)s->If->m->iml_new(s, sizeof(UTFCHAR*)*lud->numChoices*2 + 1);
    string_values[0] = utf8_to_utf16be(s, (UTF8CHAR *)lud->title, NULL);
    int j = 1;
    for (i = 0; i < lud->numChoices; ++i) {
        string_values[j++] = utf8_to_utf16be(s, (UTF8CHAR *)lud->label[i], NULL);
        string_values[j++] = utf8_to_utf16be(s, (UTF8CHAR *)lud->value[i], NULL);
    }

    aux_draw(s, AUX_LOOKUP_CLASS_NAME, 8, int_values, lud->numChoices*2 + 1, string_values);
    return 0;
}


int 
lookup_draw(UnihanIM *uim, UnihanLookupDraw *ludraw)
{
	iml_session_t *s;
    DataPerDesktop *dd;
	iml_inst *lp, *rrv = NULL;
	IMLookupDrawCallbackStruct *draw = NULL;
	int i;
	int max_len = 0;

	if (!uim) return 1;
	if (!ludraw) return 1;
	s = (iml_session_t *)uim->privateData;
	if (!s) return 1;

    // do we use this or the aux?
    dd = (DataPerDesktop *)s->desktop->specific_data;
    if (is_lookup_started(dd->aux_started)) {
        return lookup_draw_aux(uim, ludraw);
    }

	draw = (IMLookupDrawCallbackStruct *)s->If->m->iml_new(s, sizeof(IMLookupDrawCallbackStruct));
	memset(draw, 0, sizeof(IMLookupDrawCallbackStruct));
	draw->index_of_first_candidate = 0;
	draw->index_of_last_candidate = ludraw->numChoices - 1;
	draw->n_choices = ludraw->numChoices;

	draw->title = (IMText *)s->If->m->iml_new(s, sizeof(IMText));
	memset(draw->title, 0, sizeof(IMText));
	draw->title->encoding = UTF16_CODESET;
	draw->title->text.utf_chars = utf8_to_utf16be(s, (UTF8CHAR *)ludraw->title, &draw->title->char_length);
	draw->title->feedback = feedback_new(s, draw->title->char_length);

	draw->choices = (IMChoiceObject*)s->If->m->iml_new(s, draw->n_choices * sizeof(IMChoiceObject));
	for (i = 0; i < draw->n_choices; ++i) {
		IMText *label, *value;

		label = (IMText *)s->If->m->iml_new(s, sizeof(IMText));
		memset(label, 0, sizeof(IMText));
		label->encoding = UTF16_CODESET;
		label->text.utf_chars = utf8_to_utf16be(s, (UTF8CHAR *)ludraw->label[i], &label->char_length);
		// FIXME!!!
		label->feedback = feedback_new(s, label->char_length);

		value = (IMText *)s->If->m->iml_new(s, sizeof(IMText));
		memset(value, 0, sizeof(IMText));
		value->encoding = UTF16_CODESET;
		value->text.utf_chars = utf8_to_utf16be(s, (UTF8CHAR *)ludraw->value[i], &value->char_length);
		// FIXME!!!
		value->feedback = feedback_new(s, value->char_length);

		draw->choices[i].label = label;
		draw->choices[i].value = value;

		if (value->char_length > max_len)
			max_len = value->char_length;
	}
	draw->max_len = max_len;
	draw->index_of_current_candidate = ludraw->selected;

	lp = s->If->m->iml_make_lookup_draw_inst(s, draw);
	s->If->m->iml_link_inst_tail(&rrv, lp);
	s->If->m->iml_execute(s, &rrv);

	return 0;
}


static int
lookup_done_aux(UnihanIM *uim)
{
        iml_session_t *s = (iml_session_t *)uim->privateData;
        int int_values[1];
        int_values[0] = UNIHAN_AUX_LOOKUP_HIDE;
        aux_draw(s, AUX_LOOKUP_CLASS_NAME, 1, int_values, 0, NULL);
        return 0;
}


int 
lookup_done(UnihanIM *uim)
{
	iml_session_t *s;
        DataPerDesktop *dd;
	iml_inst *lp, *rrv = NULL;

	if (!uim) return 1;
	s = (iml_session_t *)uim->privateData;
	if (!s) return 1;
        
    dd = (DataPerDesktop *)s->desktop->specific_data;
    if (is_lookup_started(dd->aux_started)) {
        return lookup_done_aux(uim);
    }

	lp = s->If->m->iml_make_lookup_done_inst(s);
	s->If->m->iml_link_inst_tail(&rrv, lp);
	s->If->m->iml_execute(s, &rrv);

	return 0;
}



static int 
status_draw(UnihanIM *uim, char *string, UnihanStyleList *styles)
{
	iml_session_t *s;
        //DataPerDesktop *dd;
	iml_inst *lp, *rrv = NULL;
	IMText _imt, *imt = &_imt;

	if (!uim) return 1;
	if (!string) return 1;
	// FIXME: if (!styles) return 1;
	s = (iml_session_t *)uim->privateData;
	if (!s) return 1;

	memset(imt, 0, sizeof(IMText));
	imt->encoding = UTF16_CODESET;
	imt->text.utf_chars = utf8_to_utf16be(s, (UTF8CHAR *)string, &imt->char_length);
	// FIXME
	imt->feedback = feedback_new(s, imt->char_length);

	lp = s->If->m->iml_make_status_draw_inst(s, imt);
	s->If->m->iml_link_inst_tail(&rrv, lp);
	s->If->m->iml_execute(s, &rrv);

	return 0;
}

static int 
locale_get(UnihanIM *uim, char **locale)
{
	iml_session_t *s;

	if (!uim) return 1;
	if (!locale) return 1;
	s = (iml_session_t *)uim->privateData;
	if (!s) return 1;
    DataPerSession *ds = (DataPerSession *)s->specific_data;

	*locale = ds->locale;
	
	return 0;
}


static int 
tcsc_mode_get(UnihanIM *uim, UnihanTCSCMode *tcscMode)
{
        iml_session_t *s;
        DataPerDesktop *dd;

        if (!uim) return 1;
        if (!tcscMode) return 1;
        s = (iml_session_t *)uim->privateData;
        if (!s) return 1;

        dd = (DataPerDesktop *)s->desktop->specific_data;
        *tcscMode = dd->tcsc;
        
	return 0;
}


static int
beep(UnihanIM *uim)
{
        iml_session_t *s;

        if (!uim) return 1;
        s = (iml_session_t *)uim->privateData;
        if (!s) return 1;

        aux_beep(s);

        return 0;
}


static int 
tcsc_do_conv(UnihanIM *uim, UnihanTCSCMode tcscMode, char *orig, char **converted)
{
        iml_session_t *s;
        UTF32CHAR *u32 = NULL;

        if (!uim) return 1;
        if (!orig) return 1;
        if (!converted) return 1;
        if (tcscMode == UNIHAN_TCSC_NO_CONV) return 1;
        s = (iml_session_t *)uim->privateData;
        if (!s) return 1;

        u32 = utf8_to_utf32(s, (UTF8CHAR *)orig, NULL);

        switch (tcscMode) {
                case UNIHAN_TCSC_TO_SIMPLIFIED:
                        convert_tc_to_sc(u32);
                        break;
                case UNIHAN_TCSC_TO_TRADITIONAL:
                        convert_sc_to_tc(u32);
                        break;
                default:
                        return 1;
        }
        
        *converted = (char *)utf32_to_utf8(s, u32, NULL);
        
	return 0;
}

static int
mem_alloc(UnihanIM *uim, size_t len, void **mem)
{
	iml_session_t *s;

	if (!uim) return 1;
	if (!mem) return 1;
	if (len == 0) return 1;
	s = (iml_session_t *)uim->privateData;
	if (!s) return 1;

	*mem = s->If->m->iml_new(s, len);
	memset(*mem, 0, len);
	return 0;
}

static int
send_buffer(UnihanIM *uim, void *buf, size_t buf_size)
{
	char *a, *b;
	int i = 0;
	int num_string = 0;
	char *brk_pointer = STR_BRK_POINTER;
	struct _BufferInfo {
		char *pointer;
		struct _BufferInfo *next;
	} *buffer_info = NULL, *last_buffer_info = NULL;
	int int_value[2];
	UTFCHAR **str_value = NULL;
    iml_session_t *s;

    if (!uim)
        return 0;
    s = (iml_session_t *)uim->privateData;

	if (buf_size == 0)
		return 0;

	a = (char *)buf;
	b = (char *)buf;
	for (i=0; i<buf_size; i++) {
		if (*b == 0x0) {
			if (!buffer_info) {
				buffer_info = last_buffer_info = (struct _BufferInfo *)
						malloc(sizeof(struct _BufferInfo));
			} else {
				last_buffer_info->next = (struct _BufferInfo *)
						malloc(sizeof(struct _BufferInfo));
				last_buffer_info = last_buffer_info->next;
			}
			if (*a == 0x0) {
				last_buffer_info->pointer = STR_BRK_POINTER;
			} else {
				last_buffer_info->pointer = a;
			}
			last_buffer_info->next = NULL;
			num_string++;
			a = b + 1;
		}
		b++;
	}
	int_value[0] = UNIHAN_ENGINE_SEND_BUFFER;
	int_value[1] = buf_size;

	str_value = (UTFCHAR **)malloc(sizeof(UTFCHAR *)*num_string);
	memset(str_value, 0, sizeof(UTFCHAR *)*num_string);

	last_buffer_info = buffer_info;
	for (i=0; i<num_string; i++) {
		str_value[i] = utf8_to_utf16be(s, (UTF8CHAR *)last_buffer_info->pointer, NULL);
		last_buffer_info = last_buffer_info->next;
	}

	aux_draw(s, AUX_STATUS_CLASS_NAME, 2, int_value, num_string, str_value);

	last_buffer_info = buffer_info;
	for (i=0; i<num_string; i++) {
		last_buffer_info = buffer_info->next;
		if (strcmp(buffer_info->pointer, STR_BRK_POINTER) != 0)
			free(buffer_info);
		buffer_info = last_buffer_info;
	}
	free(str_value);
	return 1;
}

static int
config_get(UnihanIM *uim, char *key, char **value, long *len)
{
        iml_session_t *s;
        DataPerSession *ds;
        IMConfigList *imConfig;

        if (!value) return 1;
        if (!len) return 1;

        *value = NULL;
        *len = 0;

        if (!uim) return 1;
        s = (iml_session_t *)uim->privateData;
        if (!s) return 1;
        ds = (DataPerSession *)s->specific_data;
        imConfig = im_config_get(s, uim->id);
        while (imConfig) {
                if (!strcasecmp(key, imConfig->key))
                        break;
                imConfig = imConfig->next;
        }
        if (imConfig) {
                *value = imConfig->value;
                *len = strlen(imConfig->value);
                return 0;
        }
        return 1;
}


static int
config_set(UnihanIM *uim, char *key, char *value, long len)
{
	// FIXME
	return 1;
}

/*static int
config_set(UnihanIM *uim, UnihanPropertyArgList propertys)
{
	return TRUE;
}*/


static int 
client_info_get(UnihanIM *uim, UnihanClientInfoType type, char **info)
{
	iml_session_t *s;
        DataPerDesktop *dd = NULL;

	if (!uim) return 1;
	if (!info) return 1;
	s = (iml_session_t *)uim->privateData;
	if (!s) return 1;

        dd = (DataPerDesktop *)s->desktop->specific_data;

        switch (type) {
                case UserName:
                        *info = dd->username;
                        break;
                case HostName:
                        *info = dd->hostname;
                        break;
                case ProtocolType:
                        *info = dd->protocol_type;
                        break;
                case ClientType:
                        *info = dd->client_type;
                        break;
                case DisplayID:
                        *info = dd->display_id;
                        break;
                case OSName:
                        *info = dd->os_name;
                        break;
                case OSVersion:
                        *info = dd->os_version;
                        break;
                case OSArch:
                        *info = dd->os_arch;
                        break;

                default:
                        *info = NULL;
                        return 1;
        }

	return 0;
}


IMFeedbackList *
feedback_new(iml_session_t *s, int count)
{
	IMFeedbackList *fb;
	int i;

	if (s)
		fb = (IMFeedbackList *)s->If->m->iml_new(s, sizeof(IMFeedbackList) * count);
	else
		fb = (IMFeedbackList *)malloc(sizeof(IMFeedbackList) * count);
	memset(fb, 0, sizeof(IMFeedbackList) * count);

	for (i = 0; i < count; ++i) {
		IMFeedbackList *fbl = &fb[i];
		fbl->count_feedbacks = 1;
		if (s)
			fbl->feedbacks = (IMFeedback *)s->If->m->iml_new(s, sizeof(IMFeedback));
		else
			fbl->feedbacks = (IMFeedback *)malloc(sizeof(IMFeedback));
		memset(fbl->feedbacks, 0, sizeof(IMFeedback));
		fbl->feedbacks[0].type = IM_DECORATION_FEEDBACK;        // can be color feedbacks too..
		fbl->feedbacks[0].value = IMUnderline;                  // normal/reverse/underline
	}

	return fb;
}

// vi:ts=4:sw=4:nowrap:cin:expandtab
